/***************************************************************************
 *   Copyright (C) 2005 by xihe   *
 *   xihels@163.com                                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

const int udp_server_amount = 10;
const char* udp_server_list[] = {
                                    "sz.tencent.com",	// 61.144.238.145
                                    "sz2.tencent.com",	// 61.144.238.146
                                    "sz3.tencent.com",	// 202.104.129.251
                                    "sz4.tencent.com",	// 202.104.129.254
                                    "sz5.tencent.com",	// 61.141.194.203
                                    "sz6.tencent.com",	// 202.104.129.252
                                    "sz7.tencent.com",	// 202.104.129.253
                                    "202.104.129.254",
                                    "219.133.41.75",
                                    "219.133.49.167",
                                    "219.133.49.77"

                                };

const int tcp_server_amount = 4;
const char* tcp_server_list[] = {
                                    "tcpconn.tencent.com",	// 218.17.209.23
                                    "tcpconn2.tencent.com",	// 218.18.95.153
                                    "tcpconn3.tencent.com",	// 218.17.209.23
                                    "tcpconn4.tencent.com",	// 218.18.95.153
                                };


#include "Connection.h"
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <stdexcept>
using std::runtime_error;

Connection::Connection() :
    fd(-1),
    tcp(false),
    port(8000) {
	bzero(&sa, sizeof(sa));
    }
Connection::~Connection() {
    try {
	close();
    }
    catch(...){}
}

void Connection::close() {
    if ( -1 != fd) {
	::shutdown(fd, 0);
//	if (0 != ::shutdown(fd, 0)) {	
//	    throw runtime_error(strerror(errno));
//	}
	//::shutdown(fd, 1);
	::close(fd);
	fd = -1;
    }
}

void Connection::open() 
{
    close();

    initSockaddr();

    if (tcp) {
	if (-1 == (fd = socket(AF_INET, SOCK_STREAM, 0)))
	    throw runtime_error(strerror(errno));
	if ( -1 == (connect(fd, reinterpret_cast<sockaddr*>(&sa), sizeof (sa))))
	    throw runtime_error(strerror(errno));

    } else {
	if (-1 == (fd = socket(AF_INET, SOCK_DGRAM, 0)))
	    throw runtime_error(strerror(errno));
	// we use non-blocking mode to speed up connection
	if ( -1 == (fcntl(fd, F_SETFL, O_NONBLOCK)))
	    throw runtime_error(strerror(errno));
	if ( -1 == (connect(fd, reinterpret_cast<sockaddr*>(&sa), sizeof (sa))))
	    throw runtime_error(strerror(errno));
	if ( -1 == (fcntl(fd, F_SETFL, 0) ))
	    throw runtime_error(strerror(errno));
    }

}

void Connection::initSockaddr() {
    if (0 != sa.sin_addr.s_addr) 
	return;

    if (ip.empty()) {
	srand( (unsigned)time( NULL ) );
	if (tcp) 
	    ip = tcp_server_list[(rand() % tcp_server_amount)];
	else 
	    ip = udp_server_list[(rand() % udp_server_amount)];
    }
    if (0 == port)
	port =8000;
    
    char buffer[1024];
    int errornum;
    struct hostent he;
    struct hostent *phost;
    if (0 != gethostbyname_r(ip.c_str(), &he, buffer, 1024, &phost, &errornum)) {
	throw runtime_error(hstrerror(errornum));
    }

    sa.sin_family = AF_INET;
    sa.sin_port   = htons(port);
    sa.sin_addr   = *(reinterpret_cast<struct in_addr*>(he.h_addr));
}


int Connection::send(const uint8_t* buf, int len) {
    return ::send(fd, buf, len, 0);
}
int Connection::recv(uint8_t* buf) {
    if (!tcp) {
	return ::recv(fd, buf, MAX_PACKET_SIZE, 0);
    }
    uint16_t datalen;
    ::recv(fd, &datalen, sizeof (datalen), 0);
    int buflen = ntohs(datalen) - 2;
    //int recvlen;
    datalen = 0;
    return ::recv(fd, buf, buflen, MSG_WAITALL);
//    while ((buflen > 0) && (0 < (recvlen = ::recv(fd, buf, buflen, 0)))) {
//	buflen  -= recvlen;
//	buf     += recvlen;
//	datalen += recvlen;
//    }
//    return datalen;
}
